﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

using System;

using wwm.LeetCodeHelper.Infrastructure;
using wwm.LeetCodeHelper.Servers;

namespace wwm.LeetCodeHelper;

/// <summary>
/// LeetCode 帮助类
/// </summary>
public class LeetCodeHelper
{
    private static JsonDocumentOptions _jsonOptions = new JsonDocumentOptions
    {
        AllowTrailingCommas = true
    };
    private static string _solutionFolderName = "";
    private static string _currentPath = "";
    private static EndPoint _endPoint = EndPoint.CN;
    public static List<string> Data = new List<string>();

    /// <summary>
    /// 是否将题写入代码文件中, 默认false
    /// </summary>
    private static bool _includeContent = false;
    #region 参数自定义
    /// <summary>
    /// 是否将题写入代码文件中, 默认false
    /// </summary>
    /// <param name="b"></param>
    /// <returns></returns>
    public static void IsInCludeContent(bool b)
    {
        _includeContent = b;
    }
    #endregion
    #region 使用api获取数据
    /// <summary>
    /// 使用用户名密码初始化cookie
    /// </summary>
    /// <param name="username"></param>
    /// <param name="password"></param>
    /// <param name="endPoint">选择中文还是英文网站</param>
    /// <param name="SolutionFolderName"></param>
    [Obsolete]
    public static async Task InitAsync(string username, string password, EndPoint endPoint = EndPoint.CN, string SolutionFolderName = "Solutions")
    {
        await Leetcode.BuildAsync(username, password, endPoint);
        _endPoint = endPoint;
        _solutionFolderName = SolutionFolderName;
        string currentDirectory = AppDomain.CurrentDomain.BaseDirectory;// Directory.GetCurrentDirectory();
        string path = currentDirectory[..currentDirectory.LastIndexOf("bin")];
        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
        }
        _currentPath = path;
    }
    /// <summary>
    /// 使用Cookie初始化
    /// </summary>
    /// <param name="cookie"></param>
    /// <param name="endPoint"></param>
    /// <param name="SolutionFolderName"></param>
    /// <returns></returns>
    public static async Task InitAsync(string cookie, EndPoint endPoint = EndPoint.CN, string SolutionFolderName = "Solutions")
    {
        await Leetcode.BuildAsync(cookie, endPoint);
        _endPoint = endPoint;
        _solutionFolderName = SolutionFolderName;
        string currentDirectory = AppDomain.CurrentDomain.BaseDirectory;
        string path = currentDirectory[..currentDirectory.LastIndexOf("bin")];
        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
        }
        _currentPath = path;
    }
    /// <summary>
    /// 使用url或titleSlug获取题目
    /// </summary>
    /// <param name="problemUrlOrTitleSlug"></param>
    /// <returns></returns>
    public static async Task Run(string problemUrlOrTitleSlug)
    {
        await GetQuestionOrInvokeTestAsync(problemUrlOrTitleSlug);
    }
    /// <summary>
    /// 使用题号获取题目
    /// </summary>
    /// <param name="problemNum"></param>
    /// <returns></returns>
    public static async Task Run(int problemNum)
    {
        await GetQuestionOrInvokeTestAsync(problemNum);
    }
    /// <summary>
    /// 获取每日一题
    /// </summary>
    /// <returns></returns>
    public static async Task Run()
    {
        await GetTodayQuestionOrInvokeTestAsync();
    }



    /// <summary>
    /// 获取题目信息到本地
    /// </summary>
    /// <param name="problemUrlOrTitleSlug"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public static async Task GetQuestionOrInvokeTestAsync(string problemUrlOrTitleSlug)
    {
        string prefix = (_endPoint == EndPoint.CN ? Config.uri.cn.Base : Config.uri.us.Base) + "problems/";
        if (problemUrlOrTitleSlug.StartsWith("https"))
        {
            problemUrlOrTitleSlug = problemUrlOrTitleSlug.Replace(prefix, "");
        }

        problemUrlOrTitleSlug = problemUrlOrTitleSlug.Trim('/');
        string fileFullPath = Path.Combine(_currentPath, _solutionFolderName, problemUrlOrTitleSlug) + ".cs";
        //存在当天的文件内容
        if (File.Exists(fileFullPath))
        {
            Console.WriteLine("已经存在文件:" + problemUrlOrTitleSlug + ".cs");
            Console.WriteLine("开始启动测试");
            //开始启动测试
            TestResultHelper.InvokeAllTest();
            return;
        }

        string url = prefix + problemUrlOrTitleSlug + "/";
        var titles = problemUrlOrTitleSlug.Split("/");
        if (titles.Length > 0)
        {
            problemUrlOrTitleSlug = titles[0];
        }
        //string fileName = problemUrlOrTitleSlug + ".txt";
        //string fileFullName = Path.Combine(_currentPath, _cachedFileFolderName, fileName);

        //不在使用本地缓存方式, 原因: 几乎都是一次获取重复利用的概率很低, 不如直接拉取线上题目
        //如果本地已存在内容，则直接使用
        //{
        //    if (File.Exists(fileFullName))
        //    {
        //        Console.WriteLine("使用本地存在的文件:" + fileName);
        //        Data.Add(File.ReadAllText(fileFullName));
        //        GenerateFile(_solutionFolderName, url);
        //        return;
        //    }
        //}

        //请求数据
        var content = await Leetcode.GetQuestionFromUrlAsync(problemUrlOrTitleSlug);
        if (!string.IsNullOrEmpty(content))
        {
            Console.WriteLine($"{problemUrlOrTitleSlug}已从接口获取数据");
            //AnsiConsole.MarkupLine();
            //将数据加入到实例的Data中
            Data.Add(content);
            //Console.WriteLine("将数据写入文件:" + fileName);
            //WriteFile(fileFullName, content);
            GenerateFile(_solutionFolderName, url);
        }
        else
        {
            throw new Exception("获取题出错了,详细信息:" + content);
        }
    }


    public static async Task GetQuestionOrInvokeTestAsync(int problemNum)
    {
        var searchData = await Leetcode.GetProblemsByQuestionId(problemNum);

        var titleSlug = searchData?.data?.problemsetQuestionList.questions?.FirstOrDefault(a => a.frontendQuestionId == problemNum.ToString())?.titleSlug;
        if (string.IsNullOrEmpty(titleSlug))
        {
            throw new Exception("获取题出错了");
        }

        await GetQuestionOrInvokeTestAsync(titleSlug);
    }

    /// <summary>
    /// 获取当天的每日一题
    /// </summary>
    /// <returns></returns>
    public static async Task GetTodayQuestionAsync()
    {
        var titleSlug = await Leetcode.GetQuestionOfTodaTitleSlugyAsync();
        if (titleSlug == null)
        {
            throw new Exception("获取每日一题失败了");
        }
        await GetQuestionOrInvokeTestAsync(titleSlug);
    }


    /// <summary>
    /// 获取每日一题,存在每日一题时启动测试程序
    /// </summary>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public static async Task GetTodayQuestionOrInvokeTestAsync()
    {
        var titleSlug = await Leetcode.GetQuestionOfTodaTitleSlugyAsync();
        if (titleSlug == null)
        {
            throw new Exception("获取每日一题失败了");
        }
        string fileFullPath = Path.Combine(_currentPath, _solutionFolderName, titleSlug) + ".cs";
        //存在当天的文件内容
        if (File.Exists(fileFullPath))
        {
            Console.WriteLine("已经存在文件:" + titleSlug + ".cs");
            Console.WriteLine("开始启动测试");
            //开始启动测试
            TestResultHelper.InvokeAllTest();
            return;
        }
        else
        {
            Console.WriteLine("开始获取题目");
            await GetQuestionOrInvokeTestAsync(titleSlug);
        }
    }
    /// <summary>
    /// 每日一题是否已完成
    /// </summary>
    /// <returns></returns>
    public static async Task<TodayRecordDto> QueryTodayRecord()
    {
        var result = await Leetcode.queryTodayRecordAsync();
        Console.WriteLine(result.data.todayRecord[0].userStatus);
        Console.WriteLine(result.ToJson());
        var todayRecord = result?.data?.todayRecord.FirstOrDefault();
        if (todayRecord != null)
        {
            if (todayRecord.userStatus.ToLower() == "finish")
            {
                ConsoleHelper.Success("每日一题已完成：" + todayRecord.userStatus);
            }
            else
            {
                ConsoleHelper.Error("每日一题未完成：" + todayRecord.userStatus);
            }
        }
        return result;
    }

    #endregion
    #region 运行生成文件入口
    /// <summary>
    /// 生成文件
    /// </summary>
    /// <param name="SolutionFolderName"></param>
    /// <param name="url"></param>
    public static void GenerateFile(string SolutionFolderName, string url)
    {
        var enumerator = Data.GetEnumerator();
        while (enumerator.MoveNext()) GenerateFunctionFile(enumerator.Current, SolutionFolderName, includeContent: false, url: url);
    }
    #endregion
    #region 生成运行函数文件
    /// <summary>
    /// 生成函数文件
    /// </summary>
    /// <param name="contentString"></param>
    /// <param name="relativeFolderPath"></param>
    /// <param name="showContent"></param>
    /// <param name="includeContent"></param>
    /// <param name="url"></param>
    /// <returns></returns>
    /// <exception cref="ArgumentNullException"></exception>
    public static void GenerateFunctionFile(string contentString, string relativeFolderPath, bool showContent = false, bool includeContent = false, string url = "")
    {
        _includeContent = includeContent;
        var assembly = typeof(LeetCodeHelper).Assembly;

        var version = "v" + string.Join(".", assembly.GetName().Version.ToString().Split(".").Where((a, b) => b <= 2));
        string path = Path.Combine(_currentPath, relativeFolderPath);
        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
        }
        LeetcodeQuestionDTO data = JsonSerializer.Deserialize<LeetcodeQuestionDTO>(contentString);
        if (data == null || data.data == null || data.data.question == null) throw new ArgumentNullException("获取内容失败了,请重新尝试");
        using JsonDocument document = JsonDocument.Parse(data.data.question.metaData, _jsonOptions);
        //分普通函数方法和系统设计及其他(待补充)
        var is_system_design = document.RootElement.TryGetProperty("systemdesign", out JsonElement system_design);
        if (is_system_design)
        {
            //是系统设计类型
            GenerateSystemDesignFile(data, document, path, relativeFolderPath, url);
        }
        else
        {
            GenerateFunctionMethodFile(data, document, path, relativeFolderPath, url);
        }
    }
    /// <summary>
    /// 产生纯方法的文件
    /// </summary>
    /// <param name="data"></param>
    /// <param name="document"></param>
    /// <param name="path"></param>
    /// <param name="relativeFolderPath"></param>
    /// <param name="url">题库url</param>
    /// <returns></returns>
    /// <exception cref="InvalidDataException"></exception>
    private static void GenerateFunctionMethodFile(LeetcodeQuestionDTO data, JsonDocument document, string path, string relativeFolderPath, string url = "")
    {
        var hasName = document.RootElement.TryGetProperty("name", out JsonElement nameProperty);
        if (!hasName) throw new InvalidDataException("未找到文件名,请核对数据");
        var function_name = nameProperty.GetString();
        function_name = (function_name[0].ToString().ToUpper() + function_name[1..]).Trim();
        string codeSnippetsCSharpRaw = data.data.question.codeSnippets.Where(p => p.langSlug == "csharp").FirstOrDefault()?.code;
        var codeSyntaxInfo = AnalysisMethodInfo(codeSnippetsCSharpRaw);
        List<NameTypeDto> paramList = codeSyntaxInfo.paramList;
        string returnType = codeSyntaxInfo.returnType;
        //中文标题
        string title_cn = data.data.question.questionFrontendId + ". " + data.data.question.translatedTitle;
        //难度
        string difficulty = "difficulty: " + data.data.question.difficulty;
        //取示例参数
        var allCaseNodes = ParseAllPreFlag(data.data.question.translatedContent).Select(p => ParseHtmlToText(p));
        bool ignoreOrder = data.data.question.translatedContent.IndexOf("任意顺序") > -1;
        if (ignoreOrder == false)
        {
            //处理 任意和顺序文字中间有字符的情况
            var orderIndex = data.data.question.translatedContent.IndexOf("顺序");
            if (orderIndex > -1)
            {
                ignoreOrder = data.data.question.translatedContent.IndexOf("任意", Math.Max(orderIndex - 20, 0)) > -1;
            }
        }
        string fileName = data.data.question.titleSlug + ".cs";
        string fileFullName = path + Path.DirectorySeparatorChar + fileName;
        string codeSnippetsCSharp = codeSnippetsCSharpRaw;
        string dataAttributeMiddle = GenerateYieldReturnValues(paramList, returnType, allCaseNodes);
        string codeSnippets = GenerateCustomCodeSnippetsCSharp(codeSnippetsCSharp, dataAttributeMiddle, relativeFolderPath, data.data.question.questionFrontendId, title_cn, difficulty, url, function_name, data.data.question.translatedContent, ignoreOrder);
        //自定义添加函数返回值
        codeSnippetsCSharp = AddMethodRelateCode(codeSnippets);
        WriteFile(fileFullName, codeSnippetsCSharp, data.data.question.questionFrontendId, WriteFileEnum.overWritten);
    }
    /// <summary>
    /// 填充自定义内容
    /// </summary>
    /// <param name="codeSnippetsCSharp"></param>
    /// <param name="dataAttributeMiddle"></param>
    /// <param name="namespaceName"></param>
    /// <param name="questionFrontendId"></param>
    /// <param name="title"></param>
    /// <param name="difficulty"></param>
    /// <param name="url"></param>
    /// <param name="function_name"></param>
    /// <param name="translatedContent"></param>
    /// <param name="ignoreOrder"></param>
    /// <param name="hasDataAttributeFlag"></param>
    /// <param name="customCodeAfterDataAttribue"></param>
    /// <returns></returns>
    private static string GenerateCustomCodeSnippetsCSharp(string codeSnippetsCSharp, string dataAttributeMiddle, string namespaceName, string questionFrontendId, string title, string difficulty, string url, string function_name, string translatedContent = "", bool ignoreOrder = false, bool hasDataAttributeFlag = true, string customCodeAfterDataAttribue = "")
    {
        string dataAttributeBefore = @"private class Data : DataAttribute
             {";
        string dataAttributeDataString = @"public override IEnumerable<object[]> GetData()
                    {
                        ";
        string dataAttributeCtor = @"
          public Data()
            {
                IgnoreOrder = true;
            }
";

        //string dataAttributeMiddle = GenerateYieldReturnValues(paramList, returnType, allCaseNodes);
        string dataAttributeAfter = @"        }
             }";

        string dataAttributeContent = "";
        //保存题
        if (_includeContent)
        {
            dataAttributeContent = "\n/*\n"
  + ParseHtmlToText(translatedContent.Replace("<sup>", "^")
  .Replace("&lt;", "<")
  .Replace("&nbsp;", " ")
  .Replace("\n\n\n", "\n")
  .Replace("\n\n\t", "\n\t")
  .Replace("\n\n", "\n"))
  + "*/\n";
        }

        string dataAttributeFlag = hasDataAttributeFlag ? "\n[Data]" : "";
        //如果注释中有public此处就有问题
        //var lastPublicIndex = codeSnippetsCSharp.LastIndexOf("public");
        int lastPublicIndex = FindNthSearchWordIndex(codeSnippetsCSharp, "public", 2);
        codeSnippetsCSharp = codeSnippetsCSharp.Insert(lastPublicIndex - 1, dataAttributeBefore + (ignoreOrder ? dataAttributeCtor : "")
            + dataAttributeDataString + dataAttributeMiddle + dataAttributeAfter + dataAttributeContent + dataAttributeFlag + customCodeAfterDataAttribue + "\n");
        var url_doc = string.IsNullOrEmpty(url) ? "" : "/// " + url + "\n";
        codeSnippetsCSharp = "namespace " + namespaceName + ";\n" + "/// <summary>\n" + "/// " + title + "\n" + "/// " + difficulty + "\n" + url_doc + "/// </summary>\n" + codeSnippetsCSharp.Replace("Solution", function_name + "_" + questionFrontendId + "_Solution");
        return codeSnippetsCSharp;
    }

    /// <summary>
    /// 查找第N个searchWord
    /// </summary>
    /// <param name="content"></param>
    /// <param name="searchWord"></param>
    /// <param name="n"></param>
    /// <returns></returns>
    private static int FindNthSearchWordIndex(string content, string searchWord, int n)
    {
        Dictionary<string, string> commentDic = new Dictionary<string, string>()
        {
            { "/*","*/" }
        };
        //skip comment snippets
        string[] arr = content.Split("\n");
        Stack<string> stack = new();
        int allIndex = 0;
        for (int i = 0; i < arr.Length; i++)
        {
            allIndex += arr[i].Length + 1;//+1 是因为回车\n也算是一个字符

            var s = arr[i].Trim();
            if (stack.Count > 0)
            {
                var c = stack.Peek();
                if (s.TrimEnd().EndsWith(commentDic[c])) { stack.Pop(); continue; }
            }
            if (s.StartsWith("//") || s.StartsWith("#")) continue;
            if (s.StartsWith("/*")) { stack.Push("/*"); continue; }
            if (stack.Count > 0) continue;
            if (s.Contains(searchWord))
            {
                int sn = searchWord.Length;
                string currLine = arr[i];
                for (int j = 0; j < currLine.Length - sn; j++)
                {
                    if (j > 0 && currLine[j - 1] != ' ') continue;
                    if (currLine[j] == searchWord[0] && (j + sn) <= currLine.Length)
                    {
                        if (j + sn < currLine.Length && currLine[j + sn] != ' ') continue;
                        var word = currLine[j..(j + sn)];
                        if (word == searchWord)
                        {
                            n--;
                            if (n == 0)
                            {
                                return allIndex - currLine.Length + j;
                            }
                        }
                    }
                }
            }
        }
        return -1;
    }

    /// <summary>
    /// 解析语法树
    /// </summary>
    /// <param name="csharpText"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    private static (string methodName, List<NameTypeDto> paramList, string returnType) AnalysisMethodInfo(string csharpText)
    {
        //解析返回函数,提取输入参数及返回值类型
        SyntaxTree tree = CSharpSyntaxTree.ParseText(csharpText);
        CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
        if (root == null || root.RawKind != (int)SyntaxKind.CompilationUnit) throw new Exception("语法错误,无class类");
        int memberNum = root.Members.Count;
        if (memberNum == 0) throw new Exception("语法解析错误,无class类");
        if (memberNum > 1) throw new Exception("语法解析错误,class类大于1个,共" + memberNum + "个");
        MemberDeclarationSyntax member = root.Members[0];
        if (member == null || member.RawKind != (int)SyntaxKind.ClassDeclaration) throw new Exception("语法错误,无class类");
        //methods
        var methodMembers = ((ClassDeclarationSyntax)member).ChildNodes().ToArray();
        var methodMember = (MethodDeclarationSyntax)methodMembers.FirstOrDefault(p => p is MethodDeclarationSyntax);
        if (methodMember == null)
        {
            throw new Exception("未找到代码中的Method方法");
        };
        //if (methodMember.) throw new Exception("未找到method");
        //methodMember.ParameterList
        var methodName = methodMember.Identifier.ValueText;
        var paramList = methodMember.ParameterList.Parameters.Select(p => new NameTypeDto() { name = p.Identifier.ValueText, type = p.Type.ToString() }).ToList();
        var returnType = methodMember.ReturnType.ToString();
        return (methodName, paramList, returnType);
    }
    /// <summary>
    /// 解析语法并格式化
    /// 添加<see cref="ITest"/> 继承,方便测试
    /// 添加return <see cref="string"/>;返回值
    /// </summary>
    /// <param name="csharpText"></param>
    /// <returns></returns>
    private static string AddMethodRelateCode(string csharpText)
    {
        //解析返回函数,提取输入参数及返回值类型
        SyntaxTree tree = CSharpSyntaxTree.ParseText(csharpText);
        CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
        var returnWriter = new MethodReturnDefaultRewriter();
        var result = returnWriter.Visit(root);
        //格式化后输出
        //当有/* .... */注释时，NormalizeWhitespace()格式化会出错误
        if (csharpText.Contains("/*")) return result.ToFullString();
        else return CSharpSyntaxTree.ParseText(result.ToString()).GetRoot().NormalizeWhitespace().SyntaxTree.GetText().ToString();
    }
    /// <summary>
    /// 产生系统设计的文件
    /// </summary>
    /// <param name="data"></param>
    /// <param name="document"></param>
    /// <param name="path"></param>
    /// <param name="relativeFolderPath"></param>
    /// <param name="url"></param>
    /// <returns></returns>
    private static void GenerateSystemDesignFile(LeetcodeQuestionDTO data, JsonDocument document, string path, string relativeFolderPath, string url = "")
    {
        var hasName = document.RootElement.TryGetProperty("classname", out JsonElement nameProperty);
        if (!hasName) throw new InvalidDataException("未找到文件名,请核对数据");
        var function_name = nameProperty.GetString();
        function_name = function_name[0].ToString().ToUpper() + function_name[1..];
        string codeSnippetsCSharp = data.data.question.codeSnippets.Where(p => p.langSlug == "csharp").FirstOrDefault()?.code;
        string fileName = data.data.question.titleSlug + ".cs"; //function_name + "_" + data.data.question.questionFrontendId + ".cs";
        string fileFullName = path + Path.DirectorySeparatorChar + fileName;
        //中文标题
        string title_cn = data.data.question.questionFrontendId + ". " + data.data.question.translatedTitle;
        //TODO: 待完善参数调用及自动运行测试方法
        //取示例参数
        var allCaseNodes = ParseAllPreFlag(data.data.question.translatedContent).Select(p => ParseHtmlToText(p));
        List<NameTypeDto> nameTypeDtos = new List<NameTypeDto>();
        nameTypeDtos.Add(new NameTypeDto { name = "string", type = "string" });
        nameTypeDtos.Add(new NameTypeDto { name = "string", type = "string" });
        var yieldReturnString = GenerateYieldReturnValues(nameTypeDtos, "string", allCaseNodes);
        string customCode = @$"
public class Data : DataAttribute
{{
    public override IEnumerable<object[]> GetData()
    {{
        {yieldReturnString}
    }}
}}
public static void TestInvoke(){{
    var data_enumerable = new Data().GetData();
    //write your custom test code.
}}
";
        string sss = @$"
            {codeSnippetsCSharp}
        \n";
        int firstPublicMethodIndex = FindNthSearchWordIndex(codeSnippetsCSharp, "public", 2);
        codeSnippetsCSharp = codeSnippetsCSharp.Insert(firstPublicMethodIndex - 1, customCode);
        var url_doc = string.IsNullOrEmpty(url) ? "" : "/// " + url + "\n";
        //难度
        string difficulty = "/// " + "difficulty: " + data.data.question.difficulty + "\n";
        string codeSnippets = "namespace " + relativeFolderPath + ";\n" + "/// <summary>\n" + "/// " + title_cn + "\n" + difficulty + url_doc + "/// </summary>\n" + codeSnippetsCSharp.Replace("Solution", function_name + "_" + data.data.question.questionFrontendId + "_Solution");
        codeSnippets = AddMethodRelateCode(codeSnippets);
        WriteFile(fileFullName, codeSnippets, data.data.question.questionFrontendId, WriteFileEnum.overWritten);
    }

    /// <summary>
    /// 产生返回数据
    /// </summary>
    /// <param name="inputTypes"></param>
    /// <param name="returnType"></param>
    /// <param name="htmlNodes"></param>
    /// <returns></returns>
    private static string GenerateYieldReturnValues(List<NameTypeDto> inputTypes, string returnType, IEnumerable<string> htmlNodes)
    {
        string res = "";
        foreach (var s in htmlNodes)
        {
            //将输入输出参数去掉首尾\n,然后查找[解释]二字是否在开头,防止输入参数中有不是参数的例子
            var ss = s.Trim().Trim("\n".ToArray());
            //使用解释位置判断
            var descriptionIndex = ss.IndexOf("解释");
            //如果没有解释，尝试使用解析二字
            if (descriptionIndex == -1) descriptionIndex = ss.IndexOf("解析");
            //如果没有解释/解析，尝试使用提示二字
            if (descriptionIndex < 0) descriptionIndex = ss.IndexOf("提示");
            //如果没有解释/解析/提示，尝试使用说明二字
            if (descriptionIndex < 0) descriptionIndex = ss.IndexOf("说明");
            //if (descriptionIndex < 0) continue;
            var c = descriptionIndex > -1 ? s[..descriptionIndex] : s;
            //不能直接把\n去掉,\n有可能是字段的分隔符,否则导致两个变量合并成一个变量的错误.
            //c = c.Replace("\n", "").Replace("&quot;", "\"");
            c = c.Replace("&quot;", "\"");
            var inputIndex = c.IndexOf("输入");
            var outputIndex = c.IndexOf("输出");
            //如果没有输出,则跳过
            if (outputIndex < 0) continue;
            string inputText = c[(inputIndex + 3)..outputIndex];
            string outputText = outputIndex > -1 ? c[(outputIndex + 3)..] : c;
            //不能清空空格,有可能返回值中本身就有空格
            //outputText = outputText.Replace(" ", "").Replace("\"", "\\\"");
            outputText = RemmoveQuotationOuterWhiteSpace(outputText);
            outputText = TransferStringExceptHeadAndEnd(outputText);
            //处理特殊返回值,带有"返回"二字
            if (outputText.StartsWith("返回"))
            {
                var endWithComma = outputText.IndexOf('，') > -1 ? outputText.IndexOf('，') : outputText.IndexOf(',');
                outputText = endWithComma > -1 ? outputText[2..endWithComma] : outputText;
            }
            //返回值,检查是否有逗号,逗号后面是多余的返回参数
            if (outputText.Contains(','))
            {
                outputText = QueryDataBeforeComma(outputText);
            }

            //input 不能将空格都清空, 有可能参数变量中本身就有空格
            //inputText = inputText.Replace(" ", "");
            var inputTextByClean = RemmoveQuotationOuterWhiteSpace(inputText);
            inputTextByClean = TransferStringExceptHeadAndEnd(inputTextByClean);
            //分割输入参数
            var inputList = SplitParams(inputTextByClean, inputTypes);
            List<string> inputStringList = new List<string>();
            for (int i = 0; i < inputTypes.Count; i++)
            {
                inputStringList.Add(ReturnParamList(inputTypes[i].type, inputList[i]));
            }
            //output
            string outString = "";
            if (returnType == "void" && inputTypes.Count > 0 && !string.IsNullOrWhiteSpace(outputText))
            {
                outString = ReturnParamList(inputTypes[0].type, outputText);
            }
            else
            {
                outString = ReturnParamList(returnType, outputText);
            }
            res += $"yield return new object[] {{ {string.Join(",", inputStringList)}, {outString} }};\n";
        }
        return res;
    }

    /// <summary>
    /// 给定类型输出csharp语法包装后的字符串
    /// </summary>
    /// <param name="typeName"></param>
    /// <param name="inOutputText"></param>
    /// <returns></returns>
    /// <exception cref="InvalidDataException"></exception>
    private static string ReturnParamList(string typeName, string inOutputText)
    {
        string res;
        switch (typeName)
        {
            case "bool":
            case "boolean":
                res = inOutputText.ToLower();
                break;
            case "bool[]":
            case "boolean[]":
                res = "StringTo<bool[]>(" + "\"" + inOutputText.ToLower() + "\"" + ").ToArray()";
                break;
            case "bool[][]":
            case "boolean[][]":
                res = "StringTo<List<bool[]>>(" + "\"" + inOutputText.ToLower() + "\"" + ").ToArray()";
                break;
            case "list<bool>":
            case "IList<bool>":
            case "list<boolean>":
                res = "StringTo<List<bool>>(" + "\"" + inOutputText.ToLower() + "\"" + ")";
                break;
            case "list<list<bool>>":
            case "IList<IList<bool>>":
            case "list<list<boolean>>":
                res = "StringTo<List<List<bool>>>(" + "\"" + inOutputText.ToLower() + "\"" + ")";
                break;
            case "char":
            case "character":
                res = inOutputText.Replace("\"", "'");
                break;
            case "char[]":
            case "character[]":
                res = "StringTo<char[]>(" + "\"" + inOutputText.Replace("\"", "'") + "\"" + ").ToArray()";
                break;
            case "char[][]":
            case "character[][]":
                res = "StringTo<List<char[]>>(" + "\"" + inOutputText.Replace("\"", "'") + "\"" + ").ToArray()";
                break;
            case "list<char>":
            case "IList<char>":
            case "list<character>":
                res = "StringTo<List<char>>(" + "\"" + inOutputText.Replace("\"", "'") + "\"" + ")";
                break;
            case "list<list<char>>":
            case "IList<IList<char>>":
            case "list<list<character>>":
                res = "StringTo<List<List<char>>>(" + "\"" + inOutputText.Replace("\"", "'") + "\"" + ")";
                break;
            case "int":
            case "integer":
                res = inOutputText;
                break;
            case "int[]":
            case "integer[]":
                res = "StringTo<int[]>(" + "\"" + inOutputText + "\"" + ").ToArray()";
                break;
            case "int[][]":
            case "integer[][]":
                res = "StringTo<List<int[]>>(" + "\"" + inOutputText + "\"" + ").ToArray()";
                break;
            case "uint":
                res = "BinaryStringTo<uint>(" + "\"" + inOutputText + "\"" + ")";
                break;
            case "list<int>":
            case "IList<int>":
            case "list<integer>":
                res = "StringTo<List<int>>(" + "\"" + inOutputText + "\"" + ")";
                break;
            case "list<list<int>>":
            case "IList<IList<int>>":
            case "list<list<integer>>":
                res = "StringTo<List<List<int>>>(" + "\"" + inOutputText + "\"" + ").ToArray()";
                break;
            case "long":
                res = inOutputText;
                break;
            case "long[]":
                res = "StringTo<long[]>(" + "\"" + inOutputText + "\"" + ").ToArray()";
                break;
            case "long[][]":
                res = "StringTo<List<long[]>>(" + "\"" + inOutputText + "\"" + ").ToArray()";
                break;
            case "IList<long>":
                res = "StringTo<List<long>>(" + "\"" + inOutputText + "\"" + ")";
                break;
            case "IList<Ilist<long>>":
                res = "StringTo<List<List<long>>>(" + "\"" + inOutputText + "\"" + ")";
                break;
            case "string":
                if (!inOutputText.StartsWith("\"")) res = "\"" + inOutputText + "\"";
                else res = inOutputText;
                break;
            case "string[]":
                res = "StringTo<string[]>(" + "\"" + inOutputText + "\"" + ").ToArray()";
                break;
            case "string[][]":
                res = "StringTo<List<string[]>>(" + "\"" + inOutputText + "\"" + ").ToArray()";
                break;
            case "IList<string>":
            case "list<string>":
                res = "StringTo<List<string>>(" + "\"" + inOutputText + "\"" + ")";
                break;
            case "IList<IList<string>>":
            case "list<list<string>>":
                res = "StringTo<List<List<string>>>(" + "\"" + inOutputText + "\"" + ").ToArray()";
                break;
            case "double":
                res = inOutputText;
                break;
            case "double[]":
                res = "StringTo<double[]>(" + "\"" + inOutputText + "\"" + ").ToArray()";
                break;
            case "double[][]":
                res = "StringTo<List<double[]>>(" + "\"" + inOutputText + "\"" + ").ToArray()";
                break;
            case "list<double>":
            case "IList<double>":
                res = "StringTo<List<double>>(" + "\"" + inOutputText + "\"" + ")";
                break;
            case "list<list<double>>":
            case "IList<IList<double>>":
                res = "StringTo<List<List<double>>>(" + "\"" + inOutputText + "\"" + ").ToArray()";
                break;
            case "Node":
                res = "ToNodeWithString(" + "\"" + inOutputText + "\"" + ")";
                break;
            case "ListNode":
                res = "ToListNodeWithString(" + "\"" + inOutputText + "\"" + ")";
                break;
            case "ListNode[]":
                res = "ToListNodeListWithString(" + "\"" + inOutputText + "\"" + ")";
                break;
            case "TreeNode":
                res = "ToTreeNodeWithString(" + "\"" + inOutputText + "\"" + ")";
                break;
            default:
                throw new InvalidDataException($"未覆盖到 {typeName} 类型");
        }
        return res;
    }
    /// <summary>
    /// 解析<pre></pre>标签
    /// </summary>
    /// <param name="s"></param>
    /// <returns></returns>
    private static IEnumerable<string> ParseAllPreFlag(string s)
    {
        List<string> res = new List<string>();
        int i = 0;
        if (s.Length < 12) return res;
        int n = s.Length;
        while (i < n - 7)
        {
            if (s[i] == '<' && i + 5 < n)
            {
                if (s[i..(i + 5)] == "<pre>")
                {
                    i += 5;
                    StringBuilder sb = new StringBuilder();
                    while (i < n - 7 && s[i..(i + 6)] != "</pre>")
                    {
                        sb.Append(s[i]);
                        i++;
                    }
                    res.Add(sb.ToString());
                    i += 6;
                }
                else
                {
                    i++;
                }
            }
            else
            {
                i++;
            }
        }
        return res;
    }
    /// <summary>
    /// 解析html到text
    /// </summary>
    /// <param name="htmlString"></param>
    /// <returns></returns>
    private static string ParseHtmlToText(string htmlString)
    {
        foreach (var flag in _htmlFlag)
        {
            htmlString = htmlString.Replace(flag, "", StringComparison.OrdinalIgnoreCase);
        }
        return htmlString;
    }

    /// <summary>
    /// html标签
    /// </summary>
    private static readonly List<string> _htmlFlag = new List<string>()
        {
            "<p>","</p>","<b>","</b>","<code>","</code>","<ul>","</ul>","<li>","</li>","<ol>","</ol>","<strong>","</strong>","<pre>","</pre>","<sup>","</sup>","<sub>","</sub>","<div>","</div>","<a>","</a>","<abbr>","</abbr>","<span>","</span>","<label>","</label>","<em>","</em>"
        };
    /// <summary>
    /// escape character
    /// </summary>
    private static readonly Dictionary<string, string> _htmlEscapeCharacterDic = new Dictionary<string, string>
    {
        { "&lt;","<" },
        { "&gt;",">" },
        { "&quot;","," },
        { "&amp;","&" },
        { "\n&nbsp;"," " },
        { "&nbsp;"," " }
    };
    /// <summary>
    /// 去掉真实数据的无效数据
    /// </summary>
    /// <param name="s"></param>
    /// <returns></returns>
    private static string RemmoveQuotationOuterWhiteSpace(string s)
    {

        StringBuilder sb = new StringBuilder();
        bool hasQuotation = false;
        for (int i = 0; i < s.Length; i++)
        {
            if (hasQuotation)
            {
                if (s[i] == '"')
                {
                    //引号结束
                    hasQuotation = false;
                }
                sb.Append(s[i]);
            }
            else
            {
                if (s[i] == '"') hasQuotation = true;
                //处理内容中有\n的情况,只有包裹在双引号中的\n不被清除,其他地方的\n都需要被清除
                if (s[i] != ' ' && s[i] != '\n') sb.Append(s[i]);
            }
        }
        return sb.ToString();
    }

    /// <summary>
    /// 转换字符串转成标准json,处理转义,及首尾换行
    /// </summary>
    /// <param name="s"></param>
    /// <returns></returns>
    private static string TransferStringExceptHeadAndEnd(string s)
    {
        //去掉首尾的\n
        s = s.Trim("\n".ToArray());
        if (s.Length <= 2) return s;
        var keys = _htmlEscapeCharacterDic.Keys;
        string s_center = s[1..^1].Replace("\"", "\\\"");
        foreach (var escapeCode in keys)
        {
            //将html转义字符转为正常字符
            s_center = s_center.Replace(escapeCode, _htmlEscapeCharacterDic[escapeCode]);
        }
        return s[0] + s_center + s[^1];
    }
    /// <summary>
    /// 分割参数
    /// </summary>
    /// <param name="paramString"></param>
    /// <param name="nameTypeDtos"></param>
    /// <returns></returns>
    private static List<string> SplitParams(string paramString, List<NameTypeDto> nameTypeDtos)
    {
        //将转义字符变成非转义字符,如\\\" 转成\"
        paramString = Regex.Unescape(paramString);
        List<string> res = new List<string>();
        foreach (var p in nameTypeDtos)
        {
            while (true)
            {
                if (paramString.StartsWith("\n"))
                {
                    paramString = paramString.Trim("\n".ToArray());
                    continue;
                }
                if (paramString.StartsWith(","))
                {
                    paramString = paramString.Trim(',');
                    continue;
                }
                break;
            }



            if (paramString.StartsWith(p.name, StringComparison.OrdinalIgnoreCase))
            {
                //处理实际名是str 输入名是 s 情况, 此时需要将名称取到最大的字符串  str=\"dog cat cat dog\" s
                int realNameLength = 0;
                for (int i = 0; i < paramString.Length; i++)
                {
                    if (!char.IsLetterOrDigit(paramString[i]))
                    {
                        break;
                    }
                    realNameLength++;
                }
                paramString = paramString[realNameLength..];
            }
            else
            {
                //处理输入参数与示例中输入参数名称不一致情况
                var firstEqualSymbolIndex = paramString.IndexOf("=");
                if (firstEqualSymbolIndex > -1)
                {
                    var paramRealName = paramString[0..firstEqualSymbolIndex];
                    Console.WriteLine($"示例参数与函数输入参数名称不一致，已自动调整【{paramRealName}】-> 【{p.name}】");
                    paramString = paramString[firstEqualSymbolIndex..];
                }
            }

            if (paramString.StartsWith("="))
            {
                paramString = paramString[1..];
            }

            switch (paramString[0])
            {
                case '"':
                    {
                        int right = 1;
                        while (right < paramString.Length)
                        {
                            if (paramString[right].ToString() == "\"")
                            {
                                res.Add(paramString[..(right + 1)]);
                                if (paramString.Length > right - 1) paramString = paramString[(right + 1)..];
                                break;
                            }
                            right++;
                        }
                    }
                    break;
                case '(':
                    {
                        var s = MatchBracketString(paramString, '(');
                        res.Add(s);
                        paramString = paramString[s.Length..];
                    }
                    break;
                case '[':
                    {
                        var s = MatchBracketString(paramString, '[');
                        res.Add(s);
                        paramString = paramString[s.Length..];
                    }
                    break;
                case '{':
                    {
                        var s = MatchBracketString(paramString, '{');
                        res.Add(s);
                        paramString = paramString[s.Length..];
                    }
                    break;
                default:
                    //处理剩余参数字符串的开头是数字或字符串情况
                    if (Regex.IsMatch(paramString[0].ToString(), @"[\d\w\+\-]", RegexOptions.IgnoreCase))
                    {
                        StringBuilder sb = new StringBuilder();
                        //sb.Append(paramString[0..p.name.Length]);
                        int right = 0;
                        while (right < paramString.Length)
                        {
                            if (paramString[right] == ',')
                            {
                                paramString = paramString[right..];
                                break;
                            }
                            if (paramString[right..].StartsWith("\n"))
                            {
                                paramString = paramString[(right + 1)..];
                                break;
                            }
                            sb.Append(paramString[right]);
                            right++;
                        }
                        res.Add(sb.ToString());
                    }
                    break;
            }
        }
        return res.Select(p => TransferStringExceptHeadAndEnd(p)).ToList();
    }
    /// <summary>
    /// 获取标点前数据,如: 5,nums=[1,2,3] => 5
    /// </summary>
    /// <returns></returns>
    private static string QueryDataBeforeComma(string s)
    {
        if (s == null || s.Length <= 1) return s;
        var brackets = _bracketDic.Keys;
        if (brackets.Contains(s[0]))
        {
            return MatchBracketString(s, s[0]);
        }
        int indexOfBracket = s.IndexOf(',');
        if (indexOfBracket == -1) return s;
        return s[0..indexOfBracket];
    }
    /// <summary>
    /// 匹配成对的标点符号
    /// </summary>
    /// <param name="s"></param>
    /// <param name="bracket"></param>
    /// <returns></returns>
    private static string MatchBracketString(string s, char bracket)
    {
        if (s.Length == 0 || bracket.ToString().Length == 0) return "";
        Stack<char> stack = new Stack<char>();
        int left = 0;
        int right = 0;
        for (int i = 0; i < s.Length; i++)
        {
            if (stack.Count == 0 && s[i] != bracket) { right++; left++; continue; }
            if (s[i] == bracket) stack.Push(bracket);
            else if (s[i] == _bracketDic[bracket]) stack.Pop();
            if (stack.Count == 0) { right = i; break; }
        }
        return s[left..(right + 1)];
    }

    /// <summary>
    /// 数字字符串
    /// </summary>
    private static readonly List<char> _numChars = new() { '-', '+', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };

    /// <summary>
    /// 括号匹配
    /// </summary>
    private static readonly Dictionary<char, char> _bracketDic = new Dictionary<char, char>()
        {
            { '"','"' },
            { '(',')' },
            { '[',']' },
            { '{','}' }
        };

    private static readonly Dictionary<string, string> _difficulty_map = new Dictionary<string, string>()
    {
        { "Easy","简单" },
        { "Medium","中等" },
        { "Hadrd","困难" }
    };

    /// <summary>
    /// 文件写入选项
    /// </summary>
    private enum WriteFileEnum
    {
        /// <summary>
        /// 覆盖式写入
        /// </summary>
        overWritten,
        /// <summary>
        /// 如果存在则跳过
        /// </summary>
        skip,
        /// <summary>
        /// 续写式写入
        /// </summary>
        append,
        /// <summary>
        /// 运行查看将要发生的
        /// </summary>
        dryRun
    }
    /// <summary>
    /// 写入文件
    /// </summary>
    /// <param name="path"></param>
    /// <param name="s"></param>
    /// <param name="questionFrontendId"></param>
    /// <param name="writeEnumAction"></param>
    private static void WriteFile(string path, string s, string questionFrontendId = "", WriteFileEnum writeEnumAction = WriteFileEnum.overWritten)
    {
        string dir = Path.GetDirectoryName(path);
        if (!Directory.Exists(dir))
        {
            Directory.CreateDirectory(dir);
        }
        string copyFileName = null;
        switch (writeEnumAction)
        {
            case WriteFileEnum.overWritten:
                if (File.Exists(path))
                {
                    string hasFileWarning = "文件已存在,1.覆盖请输入Y(or y)或直接回车, 2.查看已存在文件的内容请输入空格, 3.手动输入文件名后缀请输入A(or a), 4.跳过请输入其他任意字符 :";
                    ConsoleHelper.Write(hasFileWarning, ConsoleColor.DarkYellow);
                    ConsoleKeyInfo key = Console.ReadKey();
                    if (key.Key == ConsoleKey.Y || key.Key == ConsoleKey.Enter)
                    {
                        File.WriteAllText(path, s);
                        Console.WriteLine();
                        ConsoleHelper.Success("文件已写入：" + path);
                    }
                    else if (key.Key == ConsoleKey.A)
                    {
                        Console.WriteLine();
                        string fileName = Path.GetFileNameWithoutExtension(path);
                        Console.Write($"输入后缀({questionFrontendId}已放入剪切板):");
                        ClipboardService.SetText(questionFrontendId);
                        string editedFileName;
                        while ((editedFileName = Console.ReadLine()).Length == 0) { }
                        editedFileName = fileName + "_" + editedFileName;
                        string directoryName = Path.GetDirectoryName(path);
                        //替换内容中的 FileName 字符串
                        s = s.Replace(fileName, editedFileName);
                        if (!editedFileName.EndsWith(".cs")) editedFileName += ".cs";

                        string fileFullPath = Path.Combine(directoryName, editedFileName);
                        File.WriteAllText(fileFullPath, s);
                        ConsoleHelper.Success("文件已写入：" + fileFullPath);
                        copyFileName = editedFileName;
                    }
                    else if (key.Key == ConsoleKey.Spacebar)
                    {
                        Console.WriteLine();
                        Console.WriteLine("文件: " + path);
                        ConsoleHelper.Success(File.ReadAllText(path));
                        WriteFile(path, s, questionFrontendId, writeEnumAction);
                    }
                    else
                    {
                        Console.WriteLine();
                        ConsoleHelper.Warn("已取消写入文件");
                    }
                }
                else
                {
                    File.WriteAllText(path, s);
                    ConsoleHelper.Success("文件已写入：" + path);
                }
                break;
            case WriteFileEnum.skip:
                if (File.Exists(path)) Console.WriteLine("已跳过已存在的文件:" + path);
                else
                {
                    File.WriteAllText(path, s);
                    ConsoleHelper.Success("文件已写入：" + path);
                }
                break;
            case WriteFileEnum.append:
                File.AppendAllText(path, s);
                ConsoleHelper.Success("文件已写入：" + path);
                break;
            case WriteFileEnum.dryRun:
                Console.WriteLine("文件内容：\n");
                ConsoleHelper.Write(s, ConsoleColor.DarkGreen);
                break;
            default:
                break;
        }
        if (copyFileName == null)
        {
            copyFileName = Path.GetFileName(path);
        }
        ClipboardService.SetText(copyFileName);
        ConsoleHelper.Success("文件名已拷贝到剪贴板: " + copyFileName);
    }
    #endregion
}